package com.emeraldparser.symbol;

import com.emeraldparser.util.Automaton;
import com.emeraldparser.util.Automatons;
import com.emeraldparser.util.HashCodes;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

public final class NegationSymbolClass extends AtomicSymbolClass {

	private final SymbolClass negatedSymbolClass;
	private final transient int hashCode;

	public NegationSymbolClass(SymbolClass negatedSymbolClass) {
		this.negatedSymbolClass = Preconditions.checkNotNull(negatedSymbolClass, "Negated symbol class must not be null");
		this.hashCode = HashCodes.classSpecificHashCode(NegationSymbolClass.class, negatedSymbolClass);
	}

	public SymbolClass getNegatedSymbolClass() {
		return negatedSymbolClass;
	}

	@Override
	public String toString() {
		return "!" + negatedSymbolClass;
	}

	@Override
	public Automaton<? extends State, ? extends SymbolClass> toAutomaton() {
		Automaton<State, SymbolClass> automaton = Automatons.create(super.toAutomaton());
		
		automaton.getAcceptVertices().remove(getAfterState());
		automaton.getAcceptVertices().add(getBeforeState());
		
		automaton.getRejectVertices().add(getAfterState());
		
		return automaton;
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (getClass() != obj.getClass()) {
			return false;
		}
		
		NegationSymbolClass other = (NegationSymbolClass) obj;
		
		return Objects.equal(negatedSymbolClass, other.negatedSymbolClass);
	}

	@Override
	public <I, O> O accept(SymbolClassVisitor<I, O> visitor, I input) {
		return visitor.visitNegationSymbolClass(this, input);
	}

}
